﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Dreamz
{
    // Used to store a "picture" made of characters, similar to a bitmap.
    public class CharMap
    {
        public CharInfo[,] Chars;
        public int Width;
        public int Height;
        public char? AlphaChar = null;

        public CharMap(int Width, int Height)
        {
            this.Width = Width;
            this.Height = Height;
            Chars = new CharInfo[Width, Height];
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    Chars[j, i].Attributes = (short)ConsoleColor.White;
                }
            }
        }
        public CharMap(int Width, int Height, CharInfo Fill)
        {
            this.Width = Width;
            this.Height = Height;
            Chars = new CharInfo[Width, Height];
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    Chars[j, i] = Fill;
                }
            }
        }
        public CharMap(string String) : this(String, ConsoleColor.White)
        {
        }
        public CharMap(string String, ConsoleColor Color)
        {
            string[] split = String.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);

            // find dimensions
            Height = split.Length;
            Width = 0;
            for (int i = 0; i < Height; i++)
                if (Width < split[i].Length)
                    Width = split[i].Length;

            Chars = new CharInfo[Width, Height];

            for (int row = 0; row < Height; row++)
            {
                int cols = split[row].Length;
                for (int col = 0; col < cols; col++)
                {
                    Chars[col, row].Char.AsciiChar = Convert.ToByte(split[row][col]);
                    Chars[col, row].Attributes = (short)Color;
                }
            }
        }
        public CharMap(string String, ConsoleColor Color, char AlphaChar) : this(String, Color)
        {
            this.AlphaChar = AlphaChar;
        }
    }

    public struct Rectangle
    {
        public int X, Y, Width, Height;

        public Rectangle(int X, int Y, int Width, int Height)
        {
            this.X = X;
            this.Y = Y;
            this.Width = Width;
            this.Height = Height;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct Coord
    {
        public short X;
        public short Y;

        public Coord(short X, short Y)
        {
            this.X = X;
            this.Y = Y;
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct CharUnion
    {
        [FieldOffset(0)]
        public char UnicodeChar;
        [FieldOffset(0)]
        public byte AsciiChar;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct CharInfo
    {
        [FieldOffset(0)]
        public CharUnion Char;
        [FieldOffset(2)]
        public short Attributes;
    }

    // A screen buffer drawing helper, used to draw strings and CharMaps anywhere on the screen.
    public class ConsoleScreenDrawer
    {
        static public ConsoleScreenDrawer S;

        public short Width, Height;

        private IntPtr Handle;
        private CharInfo[] Buffer;
        private SmallRect WriteRegion;
        private Coord BufferSize, BufferCoord;

        public ConsoleScreenDrawer(short Width, short Height)
        {
            this.Width = Width;
            this.Height = Height;
            Buffer = new CharInfo[Width * Height];
            BufferSize = new Coord(Width, Height);
            BufferCoord = new Coord(0, 0);
            WriteRegion = new SmallRect() { Left = 0, Top = 0, Right = Width, Bottom = Height };
        }

        public void Init()
        {
            Console.WindowWidth = Width;
            Console.WindowHeight = Height;
            Console.BufferWidth = Width;
            Console.BufferHeight = Height;
            Handle = GetStdHandle(STD_OUTPUT_HANDLE);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GetStdHandle(int nStdHandle);
        const int STD_OUTPUT_HANDLE = -11;

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteConsoleOutput(
          IntPtr hConsoleOutput,
          CharInfo[] lpBuffer,
          Coord dwBufferSize,
          Coord dwBufferCoord,
          ref SmallRect lpWriteRegion);

        [StructLayout(LayoutKind.Sequential)]
        public struct SmallRect
        {
            public short Left;
            public short Top;
            public short Right;
            public short Bottom;
        }

        public void Clear()
        {
            Buffer = new CharInfo[Width * Height];
        }

        public void Present()
        {
            WriteConsoleOutput(Handle, Buffer, BufferSize, BufferCoord, ref WriteRegion);
        }

        public void BufferDraw(string String, int X, int Y)
        {
            string[] rowSplit = String.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);

            int rows = rowSplit.Length;
            int cols;

            short color = (short)ConsoleColor.White;

            for (int row = Y > 0 ? 0 : -Y; row < rows && Y + row < Height; row++)
            {
                cols = rowSplit[row].Length;
                for (int col = X > 0 ? 0 : -X; col < cols && X + col < Width; col++)
                {
                    if (rowSplit[row][col] != 0)
                    {
                        Buffer[Width * Y + Width * row + X + col].Char.AsciiChar = (byte)rowSplit[row][col];
                        Buffer[Width * Y + Width * row + X + col].Attributes = color;
                    }
                }
            }
        }
        public void BufferDraw(string String, int X, int Y, ConsoleColor Color)
        {
            string[] rowSplit = String.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);

            int rows = rowSplit.Length;
            int cols;

            short color = (short)Color;

            for (int row = Y > 0 ? 0 : -Y; row < rows && Y + row < Height; row++)
            {
                cols = rowSplit[row].Length;
                for (int col = X > 0 ? 0 : -X; col < cols && X + col < Width; col++)
                {
                    if (rowSplit[row][col] != 0)
                    {
                        Buffer[Width * Y + Width * row + X + col].Char.AsciiChar = (byte)rowSplit[row][col];
                        Buffer[Width * Y + Width * row + X + col].Attributes = color;
                    }
                }
            }
        }
        public void BufferDraw(string String, int X, int Y, ConsoleColor Color, char AlphaChar)
        {
            string[] rowSplit = String.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);

            int rows = rowSplit.Length;
            int cols;

            short color = (short)Color;

            for (int row = Y > 0 ? 0 : -Y; row < rows && Y + row < Height; row++)
            {
                cols = rowSplit[row].Length;
                for (int col = X > 0 ? 0 : -X; col < cols && X + col < Width; col++)
                {
                    if (rowSplit[row][col] != 0 && rowSplit[row][col] != AlphaChar)
                    {
                        Buffer[Width * Y + Width * row + X + col].Char.AsciiChar = (byte)rowSplit[row][col];
                        Buffer[Width * Y + Width * row + X + col].Attributes = color;
                    }
                }
            }
        }
        public void BufferDraw(CharMap CharMap, int X, int Y)
        {
            int rows = CharMap.Height;
            int cols = CharMap.Width;
            if (CharMap.AlphaChar == null)
            {
                for (int row = 0; row < rows && Y + row < Height; row++)
                {
                    for (int col = 0; col < cols && X + col < Width; col++)
                    {
                        if (CharMap.Chars[col, row].Char.AsciiChar != 0)
                            Buffer[Width * Y + Width * row + X + col] = CharMap.Chars[col, row];
                    }
                }
            }
            else
            {
                for (int row = 0; row < rows && Y + row < Height; row++)
                {
                    for (int col = 0; col < cols && X + col < Width; col++)
                    {
                        if (CharMap.Chars[col, row].Char.AsciiChar != 0 && CharMap.Chars[col, row].Char.AsciiChar != CharMap.AlphaChar)
                            Buffer[Width * Y + Width * row + X + col] = CharMap.Chars[col, row];
                    }
                }
            }
        }
        //public void BufferDraw(char[] Chars, int X, int Y)
        //{
        //    if (Y <= Height)
        //    {
        //        for (int i = 0; i < Chars.Length && X + i < Width; i++)
        //        {
        //            Buffer[Y * Width + X + i].Char.AsciiChar = (byte)Chars[i];
        //            Buffer[Y * Width + X + i].Attributes = (short)ConsoleColor.White;
        //        }
        //    }
        //}
        //public void BufferDraw(char[] Chars, int X, int Y, ConsoleColor Color)
        //{
        //    if (Y <= Height)
        //    {
        //        for (int col = 0; col < Chars.Length && X + col < Width; col++)
        //        {
        //            Buffer[Y * Width + X + col].Char.AsciiChar = (byte)Chars[col];
        //            Buffer[Y * Width + X + col].Attributes = (short)Color;
        //        }
        //    }
        //}
        //public void BufferDraw(char[,] Chars, int X, int Y)
        //{
        //    int charsX = Chars.GetLength(0);
        //    int charsY = Chars.GetLength(1);
        //    short white = (short)ConsoleColor.White;
        //    for (int row = 0; row < charsY && Y + row < Height; row++)
        //    {
        //        for (int col = 0; col < charsX && X + col < Width; col++)
        //        {
        //            Buffer[Width * Y + Width * row + X + col].Char.AsciiChar = (byte)Chars[col, row];
        //            Buffer[Width * Y + Width * row + X + col].Attributes = white;
        //        }
        //    }
        //}
        //public void BufferDraw(char[,] Chars, int X, int Y, ConsoleColor Color)
        //{
        //    int charsX = Chars.GetLength(0);
        //    int charsY = Chars.GetLength(1);
        //    for (int row = 0; row < charsY && Y + row < Height; row++)
        //    {
        //        for (int col = 0; col < charsX && X + col < Width; col++)
        //        {
        //            Buffer[Width * Y + Width * row + X + col].Char.AsciiChar = (byte)Chars[col, row];
        //            Buffer[Width * Y + Width * row + X + col].Attributes = (short)Color;
        //        }
        //    }
        //}

        public void Draw(string String, int X, int Y)
        {
            BufferDraw(String, X, Y);
            Present();
        }
        public void Draw(string String, int X, int Y, ConsoleColor Color)
        {
            BufferDraw(String, X, Y, Color);
            Present();
        }

        public void Show(string String, int X, int Y)
        {
            Clear();
            BufferDraw(String, X, Y);
            Present();
        }
    }
}
